package com.kl.oauth2.controller;


import com.kl.oauth2.util.URLParamsUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

/**
 * 使用此类来模拟【授权服务】
 */
public class OauthController extends HttpServlet {

    //模拟授权码、令牌等数据存储
    public static Map<String, String> codeMap = new HashMap<>();

    public static Map<String, String[]> codeScopeMap = new HashMap<>();

    public static Map<String, String> tokenMap = new HashMap<>();

    public static Map<String, String[]> tokenScopeMap = new HashMap<>();

    public static Map<String, String> refreshTokenMap = new HashMap<>();

    public static Map<String, String> appMap = new HashMap<>();

    public static Map<String, String> reqIdMap = new HashMap<>();


    static {
        //模拟第三方软件注册之后的数据库存储
        appMap.put("app_id", "APP_ID_RABBIT");
        appMap.put("app_secret", "APP_SECRET_RABBIT");
        appMap.put("redirect_uri", "http://localhost:8080/appServlet");
        appMap.put("scope", "today history");

    }

    @PostMapping(value = "oauthServlet")
    public void acceptAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {

        System.out.println("start accept post req, generate access_token");
        String reqType = request.getParameter("reqType");
        String grantType = request.getParameter("grant_type");
        String appId = request.getParameter("app_id");
        String appSecret = request.getParameter("app_secret");
        String responseType = request.getParameter("response_type");
        String redirectUri = request.getParameter("redirect_uri");

        // 处理用户点击approve按钮动作
        if ("approve".equals(reqType)) {
            // 假设一定能够获取到值
            String reqId = request.getParameter("reqId");

            if (!reqIdMap.containsKey(reqId)) {
                return;
            }

            if ("code".equals(responseType)) {

                String[] rscope = request.getParameterValues("rscope");

                // 验证权限范围，对又验证一次
                if (!checkScope(rscope)) {
                    //超出注册的权限范围
                    System.out.println("out of scope ...");
                    return;
                }

                // 模拟登陆用户为USER_TEST
                String code = generateCode(appId, "USER_TEST");
                // 授权范围与授权码做绑定
                codeScopeMap.put(code, rscope);

                Map<String, String> params = new HashMap<>();
                params.put("code", code);
                // 构造第三方软件的回调地址，并重定向到该地址
                String toAppUrl = URLParamsUtil.appendParams(redirectUri, params);
                // 授权码流程的【第二次】重定向
                response.sendRedirect(toAppUrl);
            }
        }

        // 处理授权码流程中的 颁发访问令牌环节
        if ("authorization_code".equals(grantType)) {

            if (!appMap.get("app_id").equals(appId)) {
                response.getWriter().write("app_id is not available");
                return;
            }

            if (!appMap.get("app_secret").equals(appSecret)) {
                response.getWriter().write("app_secret is not available");
                return;
            }

            String code = request.getParameter("code");

            // 验证code值
            if (!isExistCode(code)) {
                return;
            }
            // 授权码一旦被使用，须要立即作废
            codeMap.remove(code);

            System.out.println("start generate access_token");
            // 生成访问令牌access_token的值
            String accessToken = generateAccessToken(appId, "USER_TEST");
            // 授权范围与访问令牌绑定
            tokenScopeMap.put(accessToken, codeScopeMap.get(code));
            // 生成刷新令牌refresh_token的值
            String refreshToken = generateRefreshToken(appId, "USER_TEST");


            // TODO: 将accessToken和refreshToken做绑定 ，将refreshToken和codeScopeMap做绑定

            response.getWriter().write(accessToken + "|" + refreshToken);

        } else if ("refresh_token".equals(grantType)) {//处理刷新令牌请求环节

            if (!"APP_ID_TEST".equals(appId)) {
                response.getWriter().write("app_id is not available");
                return;
            }

            if (!"APP_SECRET_TEST".equals(appSecret)) {
                response.getWriter().write("app_secret is not available");
                return;
            }


            String refresh_token = request.getParameter("refresh_token");
            // 该refresh_token值不存在
            if (!refreshTokenMap.containsKey(refresh_token)) {
                return;
            }

            String appStr = refreshTokenMap.get("refresh_token");
            // 该refresh_token值 不是颁发给该 第三方软件的
            if (!appStr.startsWith(appId + "|" + "USER_TEST")) {
                return;
            }
            // 生成访问令牌access_token的值
            String accessToken = generateAccessToken(appId, "USER_TEST");

            // TODO: 删除旧的access_token 、删除旧的refresh_token、生成新的refresh_token

            response.getWriter().write(accessToken);

        }

    }

    @GetMapping(value = "oauthServlet")
    public String toApprove(HttpServletRequest request, HttpServletResponse response) {

        String responseType = request.getParameter("response_type");
        String redirectUri = request.getParameter("redirect_uri");
        String appId = request.getParameter("app_id");
        String scope = request.getParameter("scope");

        System.out.println("8080 GET responseType: " + responseType);

        if (!appMap.get("app_id").equals(appId)) {
            return "/index";
        }

        if (!appMap.get("redirect_uri").equals(redirectUri)) {
            return "/index";
        }

        // 验证第三方软件请求的权限范围是否与当时注册的权限范围一致
        if (!checkScope(scope)) {
            // 超出注册的权限范围
            return "/index";
        }

        // 生成页面reqId
        String reqId = String.valueOf(System.currentTimeMillis());
        // 保存该reqId值
        reqIdMap.put(reqId, reqId);

        request.setAttribute("reqId", reqId);
        request.setAttribute("response_type", responseType);
        request.setAttribute("redirect_uri", redirectUri);
        request.setAttribute("app_id", appId);

        // 跳转到授权页面
        return "/approve";
        // 至此颁发授权码code的准备工作完毕
    }


    /**
     * 生成code值
     */
    private String generateCode(String appId, String user) {
        Random r = new Random();
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            strb.append(r.nextInt(10));
        }
        String code = strb.toString();
        //在这一篇章我们仅作为演示用，实际这应该是一个全局数据库,有效期官方建议是10分钟
        codeMap.put(code, appId + "|" + user + "|" + System.currentTimeMillis());

        return code;
    }


    /**
     * 生成access_token值
     */
    private String generateAccessToken(String appId, String user) {

        String accessToken = UUID.randomUUID().toString();
        // 1天时间过期
        String expires_in = "1";
        // 在这一篇章我们仅作为演示用，实际这应该是一个全局数据库,并且有有效期
        tokenMap.put(accessToken, appId + "|" + user + "|" + System.currentTimeMillis() + "|" + expires_in);
        return accessToken;
    }


    /**
     * 生成refresh_token值
     */
    private String generateRefreshToken(String appId, String user) {
        String refreshToken = UUID.randomUUID().toString();
        // 在这一篇章我们仅作为演示用，实际这应该是一个全局数据库,并且有有效期
        refreshTokenMap.put(refreshToken, appId + "|" + user + "|" + System.currentTimeMillis());
        return refreshToken;
    }

    /**
     * 是否存在code值
     */
    private boolean isExistCode(String code) {
        return codeMap.containsKey(code);
    }


    /**
     * 验证权限
     */
    private boolean checkScope(String scope) {

        System.out.println("appMap size: " + appMap.size());
        System.out.println("appMap scope: " + appMap.get("scope"));
        System.out.println("scope: " + scope);

        // 简单模拟权限验证
        return appMap.get("scope").contains(scope);
    }


    private boolean checkScope(String[] rscope) {
        StringBuilder scope = new StringBuilder();

        for (String s : rscope) {
            scope.append(s);
        }
        // 简单模拟权限验证
        return appMap.get("scope").replace(" ", "").contains(scope.toString());
    }


    public static void main(String[] args) {
        System.out.println(UUID.randomUUID());
    }

}
